namespace Brjnk.CoordinateSystem2D.Layers
{
    using System;
    using System.Windows;
    using System.Windows.Media;

    /// <summary>
    /// </summary>
    public class GridVisual : FrameworkElement
    {
        private StreamGeometry X1 = new StreamGeometry();
        private StreamGeometry X2 = new StreamGeometry();
        private StreamGeometry X3 = new StreamGeometry();
        private StreamGeometry X4 = new StreamGeometry();
        private StreamGeometry Y1 = new StreamGeometry();
        private StreamGeometry Y2 = new StreamGeometry();
        private StreamGeometry Y3 = new StreamGeometry();
        private StreamGeometry Y4 = new StreamGeometry();

        private StreamGeometryContext X1Context;
        private StreamGeometryContext X2Context;
        private StreamGeometryContext X3Context;
        private StreamGeometryContext X4Context;
        private StreamGeometryContext Y1Context;
        private StreamGeometryContext Y2Context;
        private StreamGeometryContext Y3Context;
        private StreamGeometryContext Y4Context;

        private GridPens pens;

        private DrawingVisual visual = new DrawingVisual();
        private ViewInfoBase view;

        public GridVisual(GridPens pens)
        {
            SetPens(pens);

            this.SizeChanged += new SizeChangedEventHandler(GridVisual_SizeChanged);
        }

        public void SetView(ViewInfoBase view)
        {
            this.view = view;
            RenderX();
            RenderY();
        }

        public void SetPens(GridPens pens)
        {
            this.pens = pens;

            using (var context = visual.RenderOpen())
            {
                context.DrawGeometry(null, pens.XLarge, X1);
                context.DrawGeometry(null, pens.XBig, X2);
                context.DrawGeometry(null, pens.XMedium, X3);
                context.DrawGeometry(null, pens.XSmall, X4);
                context.DrawGeometry(null, pens.YLarge, Y1);
                context.DrawGeometry(null, pens.YBig, Y2);
                context.DrawGeometry(null, pens.YMedium, Y3);
                context.DrawGeometry(null, pens.YSmall, Y4);
            }
        }

        private void GridVisual_SizeChanged(object sender, SizeChangedEventArgs e)
        {
            RenderX();
            RenderY();
        }

        protected override int VisualChildrenCount
        {
            get
            {
                return 1;
            }
        }

        protected override Visual GetVisualChild(int index)
        {
            return visual;
        }

        private void OpenRenderX()
        {
            X1Context = X1.Open();
            X2Context = X2.Open();
            X3Context = X3.Open();
            X4Context = X4.Open();
        }

        private void OpenRenderY()
        {
            Y1Context = Y1.Open();
            Y2Context = Y2.Open();
            Y3Context = Y3.Open();
            Y4Context = Y4.Open();
        }

        private void RenderX()
        {
            if (dataX == null || view == null) return;
            OpenRenderX();
            foreach (var l in dataX.Labels)
            {
                double relativePos = view.FromInternalToRelativeX(l.Value);
                switch (l.Size)
                {
                    case LabelSize.Small:
                        X4Context.BeginFigure(new Point(relativePos, 0), false, false);
                        X4Context.LineTo(new Point(relativePos, this.ActualHeight), true, false);
                        break;

                    case LabelSize.Medium:
                        X3Context.BeginFigure(new Point(relativePos, 0), false, false);
                        X3Context.LineTo(new Point(relativePos, this.ActualHeight), true, false);
                        break;

                    case LabelSize.Big:
                        X2Context.BeginFigure(new Point(relativePos, 0), false, false);
                        X2Context.LineTo(new Point(relativePos, this.ActualHeight), true, false);
                        break;

                    case LabelSize.Large:
                        X1Context.BeginFigure(new Point(relativePos, 0), false, false);
                        X1Context.LineTo(new Point(relativePos, this.ActualHeight), true, false);
                        break;

                    default:
                        break;
                }
            }
            CloseRenderX();
        }

        private void RenderY()
        {
            if (dataY == null || view == null) return;
            OpenRenderY();
            foreach (var l in dataY.Labels)
            {
                double relativePos = view.FromInternalToRelativeY(l.Value);
                switch (l.Size)
                {
                    case LabelSize.Small:
                        Y4Context.BeginFigure(new Point(0, relativePos), false, false);
                        Y4Context.LineTo(new Point(this.ActualWidth, relativePos), true, false);
                        break;

                    case LabelSize.Medium:
                        Y3Context.BeginFigure(new Point(0, relativePos), false, false);
                        Y3Context.LineTo(new Point(this.ActualWidth, relativePos), true, false);
                        break;

                    case LabelSize.Big:
                        Y2Context.BeginFigure(new Point(0, relativePos), false, false);
                        Y2Context.LineTo(new Point(this.ActualWidth, relativePos), true, false);
                        break;

                    case LabelSize.Large:
                        Y1Context.BeginFigure(new Point(0, relativePos), false, false);
                        Y1Context.LineTo(new Point(this.ActualWidth, relativePos), true, false);
                        break;

                    default:
                        throw new ArgumentOutOfRangeException("size");
                }
            }
            CloseRenderY();
        }

        public void CloseRenderX()
        {
            X1Context.Close();
            X2Context.Close();
            X3Context.Close();
            X4Context.Close();
            X1Context = null;
            X2Context = null;
            X3Context = null;
            X4Context = null;
            InvalidateVisual();
        }

        public void CloseRenderY()
        {
            Y1Context.Close();
            Y2Context.Close();
            Y3Context.Close();
            Y4Context.Close();
            Y1Context = null;
            Y2Context = null;
            Y3Context = null;
            Y4Context = null;
            InvalidateVisual();
        }

        private GetAxisLabelsResult dataX;
        private GetAxisLabelsResult dataY;

        public void SetDataX(GetAxisLabelsResult labels)
        {
            dataX = labels;
            RenderX();
        }

        public void SetDataY(GetAxisLabelsResult labels)
        {
            dataY = labels;
            RenderY();
        }
    }
}